/*! 
 * jquery.ux.subselect - v 1.0.0 - http://jquery-ux.com/
 * Copyright (c) 2010 Michael Helgeson, Three Dub Media
 * Open Source MIT License - http://jquery-ux.com/license 
 */
// Created: 2007-04-06
// Updated: 2010-06-07
// REQUIRES: jquery 1.3, jquery.ux

;(function( $, undefined ){ // confine scope

$.ux( "subselect", { 
	version: "1.0.0",
	expose: "value", // + enable disable destroy
	defaults: {
		classroot: "ux-subselect", // classname(s) to add to generated <select>s
		formatText: null, // function(s){ return s.split('#')[1]; }, // return <option> value or null for index
		formatValue: null // function(s){ return s.split('#')[0]; } // return <option> text or null for unformatted value
	},
	create: function( elem, opts ){
		// store args, extend the instance
		$.extend( this, { elem:elem, $elem:$( elem ), opts:opts });
		// normalize the formatters...
		this.formatText = opts.formatText || noformat;
		this.formatValue = opts.formatValue || noformat;
		// finalize the markup...
		this.select = '<select class="'+ this.classify("select") +'"></select><span></span>';
		// initialize the root select element
		this.$root = this.subselect( opts.map ).appendTo( this.$elem );
		this.value( opts.value );
	},
	destroy: function(){
		this.$root.remove().empty();
	},
	disable: function( disabled ){
		$(':input',this.$root).andSelf().attr('disabled', disabled );
	},
	methods: {
		subselect: function( map ){
			if ( !map ) return; 
			var attr, options = [], $select = $( this.select ), self = this;
			for ( attr in map ){ 
				$select[0].options[ options.length ] = new Option( this.formatText( attr ), this.formatValue( attr ) );
				options.push( map[ attr ] );	
			}
			this.change( $select.eq(1), options[0] ); // trigger "change"
			return $select.eq( 0 ).bind( "change", function(){
				if ( this.disabled ) return; 
				var $span = $( this ).next('span');
				// MSIE still reports a parent node for removed elements.
				if ( $.browser.msie ) $span.find(":input").addClass( self.classify("msie-orphan") );
				self.change( $span.empty(), options[ this.selectedIndex ] );										 
			}).end();
		},
		change: function( $span, map ){
			if ( this.disabled ) return; // end of the line...
			if ( map ){
				if ( map.constructor == Array ) // do each item...
					for ( var i = 0; i < map.length; i++ ) 
						this.change( $span, map[i] );
				else if ( $.isFunction( map ) ) map( $span ); // execute callback
				else if ( typeof map == "object" && !map.jquery ) $span.append( this.subselect( map ) ); // chain
				else $span.append( map ); // string, number, jquery...
			}
			this.publish("change"); // "subselect:change"
		},
		value: function( val ){
			// read the value
			if ( val === undefined ){
				val = [ this.$root.val() ]; // root <select>
				this.$root.eq(1).find(":input").each(function(){ // each <subselect>
					val.push( $( this ).val() ); 
				}); 
				return val;
			}
			// set the value
			var $elem = this.$root.eq( 0 ); 
			if ( val.constructor == Array ) 
				while ( val.length && $elem.length ){
					$elem = $elem.eq( 0 ).val( val.shift() || "" ).trigger("change").end().slice( 1 );
					if ( !$elem.length || !$elem[0].parentNode || $elem.is("."+ this.classify("msie-orphan") ) )// find next cascade
						$elem = $elem.end().next("span").find(":input");
				}
			else $elem.val( val ).trigger( "change" );
		}
	}
});

function noformat( arg ){ 
	return arg; 
};

})( jQuery ); // secure the $ jQuery alias